10. K8S之svc
回顾一下之前的内容,就会发现,之前创建好的Pod,没有提供一个合理的访问方式
如上图:
如果外部环境想要访问Pod的提供的服务,就算Pod提供了访问,那么也只能一个个的访问:
而且还会有另外一种情况:
当某一个Pod死掉之后,就算Deployment将Pod的数量恢复到了副本的期望数值,Client也访问不了新的Pod的了。
此时,就需要介绍K8S另外一个控制器:service(svc)
介绍
Service(简称svc) 提供了一个统一的服务访问入口以及服务代理和发现机制,用户不需要了解后台Pod是如何运行。Service通过Label找到Pod组。
Kubernetes Service 定义了这样一种抽象:逻辑上的一组 Pod,一种可以访问它们的策略 —— 通常称为微服务。
在 Kubernetes 集群中,每个 Node 运行一个 kube-proxy 进程。 kube-proxy 负责为 Service 实现了一种 VIP(虚拟 IP)的形式,而不是 ExternalName 的形式。
例如通过定义一个RS启动了4个pod,如何让外部能优雅的调用到这几个pod提供的服务?由于pod的IP地址可能随时发生变化(pod不健康或期间被重启或被重建等),在pod的外层使用nginx等进行负载也就不太方便了,而Service 就是解决这个问题而存在的:
可以根据pod的标签将对应的所有pod纳入一个负载均衡组的形式,通过kube-proxy 进行数据转发,提供一个外部到pod的唯一入口而不用关心pod的变动,进而实现和外部的调用通信。
代理模式
userspace 代理模式(基本不用了)
这种模式,kube-proxy 会监视 Kubernetes 控制平面对 Service 对象和 Endpoints 对象的添加和移除操作。 对每个 Service,它会在本地 Node 上打开一个端口(随机选择)。 任何连接到“代理端口”的请求,都会被代理到 Service 的后端 Pods 中的某个上面(如 Endpoints 所报告的一样)。 使用哪个后端 Pod,是 kube-proxy 基于 SessionAffinity 来确定的。
最后,它配置 iptables 规则,捕获到达该 Service 的 clusterIP(是虚拟 IP) 和 Port 的请求,并重定向到代理端口,代理端口再代理请求到后端Pod。
默认情况下,用户空间模式下的 kube-proxy 通过轮转算法选择后端。
iptables 代理模式(开销较大)
这种模式,kube-proxy 会监视 Kubernetes 控制节点对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会配置 iptables 规则,从而捕获到达该 Service 的 clusterIP 和端口的请求,进而将请求重定向到 Service 的一组后端中的某个 Pod 上面。 对于每个 Endpoints 对象,它也会配置 iptables 规则,这个规则会选择一个后端组合。
默认的策略是,kube-proxy 在 iptables 模式下随机选择一个后端。
使用 iptables 处理流量具有较低的系统开销,因为流量由 Linux netfilter 处理, 而无需在用户空间和内核空间之间切换。 这种方法也可能更可靠。
如果 kube-proxy 在 iptables 模式下运行,并且所选的第一个 Pod 没有响应, 则连接失败。 这与用户空间模式不同:在这种情况下,kube-proxy 将检测到与第一个 Pod 的连接已失败, 并会自动使用其他后端 Pod 重试。
你可以使用 Pod 就绪探测器 验证后端 Pod 可以正常工作,以便 iptables 模式下的 kube-proxy 仅看到测试正常的后端。 这样做意味着你避免将流量通过 kube-proxy 发送到已知已失败的 Pod。
IPVS模式(目前最合理)
在 ipvs 模式下,kube-proxy 监视 Kubernetes 服务和端点,调用 netlink 接口相应地创建 IPVS 规则, 并定期将 IPVS 规则与 Kubernetes 服务和端点同步。 该控制循环可确保IPVS 状态与所需状态匹配。访问服务时,IPVS 将流量定向到后端Pod之一。
IPVS代理模式基于类似于 iptables 模式的 netfilter 挂钩函数, 但是使用哈希表作为基础数据结构,并且在内核空间中工作。 这意味着,与 iptables 模式下的 kube-proxy 相比,IPVS 模式下的 kube-proxy 重定向通信的延迟要短,并且在同步代理规则时具有更好的性能。 与其他代理模式相比,IPVS 模式还支持更高的网络流量吞吐量。
IPVS 提供了更多选项来平衡后端 Pod 的流量。
要在 IPVS 模式下运行 kube-proxy,必须在启动 kube-proxy 之前使 IPVS 在节点上可用。
当 kube-proxy 以 IPVS 代理模式启动时,它将验证 IPVS 内核模块是否可用。 如果未检测到 IPVS 内核模块,则 kube-proxy 将退回到以 iptables 代理模式运行。
所有节点安装:
1 | yum install -y ipvsadm |
master端修改为ipvs模式
1 | kubectl edit cm kube-proxy -n kube-system |
master端更新kube-proxy
1 | kubectl get pod -n kube-system |grep kube-proxy | awk '{system("kubectl delete pod "$1" -n kube-system")}' |
无头服务
Headless Service不需要分配一个IP, 而是直接以DNS记录的方式解析出被代理Pod的IP地址。 遇到这种情况,可以通过指定 Cluster IP(spec.clusterIP
)的值为 "None"
来创建 Headless Service
。
对这无头 Service 并不会分配 Cluster IP,kube-proxy 不会处理它们, 而且平台也不会为它们进行负载均衡和路由。 DNS 如何实现自动配置,依赖于 Service 是否定义了选择算符。
1 | # demo.yml |
域名格式:
1 | (servicename).$(namespace).svc.cluster.local |
发布服务
svc有以下4种类型:
1. ClusterIP(默认)
通过集群的内部 IP 暴露服务,选择该值时服务只能够在集群内部访问。 这也是默认的 ServiceType。
2. NodePort(没有ingress情况下的,常见的暴露服务的方式)
通过每个节点上的 IP 和静态端口(NodePort)暴露服务。 NodePort 服务会路由到自动创建的 ClusterIP 服务。 通过请求 <节点 IP>:<节点端口>
,你可以从集群的外部访问一个 NodePort 服务。
1 | # demo.yml |
3. LoadBalancer(公有云的暴露方式)
LoadBalancer类型的service 是可以实现集群外部访问服务的另外一种解决方案。不过并不是所有的k8s集群都会支持,大多是在公有云托管集群中会支持该类型。负载均衡器是异步创建的,关于被提供的负载均衡器的信息将会通过Service
的status.loadBalancer
字段被发布出去。
1 | # demo.yml |
如图:
访问方式:
1 | kuebctl get svc |
4. ExternalName
类型为 ExternalName 的service将服务映射到 DNS 名称,而不是典型的选择器,例如my-service
或者cassandra
。 您可以使用spec.externalName
参数指定这些服务。
创建 ExternalName 类型的服务的 yaml 如下:
1 | # demoExternalName.yml |
5. 引入外部IP(externalIPs)
如果外部的 IP 路由到集群中一个或多个 Node 上,Kubernetes Service 会被暴露给这些 externalIPs。 通过外部 IP(作为目的 IP 地址)进入到集群,打到 Service 的端口上的流量, 将会被路由到 Service 的 Endpoint 上。 externalIPs 不会被 Kubernetes 管理,它属于集群管理员的职责范畴。
1 | # demoExternalIPs.yml |